/*
* 动态或者静态，一般使用动态申请设备号，
* 然后定义一个cdev的指针，使用cdev_alloc分配并且初始化
* 然后是cdev_init将相关的操作函数和cdev绑定在一起，
* 然后是使用cdev_add将cdev和设备号绑定并且添加到系统中去，
* 引脚使用的话就是在模块安装的时候执行完cedv上述操作之后再gpio申请资源，也可以是先申请gpio资源之后再初始化cedv
* 配置输出模式，在cdev绑定的操作的函数中有对应的gpio的设置引脚函数
* 大概的使用流程是
* 用户空间使用open,write操作对应的设备节点文件，设备节点文件和对应的设备号绑定
* 找到对应的设备号，也就找到了对应的cdev结构体，也就找到对应的操作函数
*/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/cdev.h>

MODULE_LICENSE("GPL");

#define LED1 74

static struct cdev* ledCdev;
static dev_t devt;

int led_open(struct inode *inode, struct file *filePtr)
{
    gpio_set_value(LED1,1);
    return 0;
}

int led_release(struct inode* inode, struct file* filePtr)
{
    gpio_set_value(LED1,0);
    return 0;
}

static struct file_operations ledFops = 
{
    .owner = THIS_MODULE,//在模块被使用时，它不能被卸载。只有在所有使用它的对象都释放了引用后，模块才能安全地被卸载。
    .open = led_open,
    .release = led_release
};

static int __init led_drv_init(void)
{
    alloc_chrdev_region(&devt,0,1,"led1");//分配设备号
    ledCdev = cdev_alloc();//分配字符设备对象，并且初始化
    cdev_init(ledCdev,&ledFops);//将字符设备对象和操作函数绑定
    cdev_add(ledCdev,devt,1);//将字符设备添加到系统中

    if(gpio_request(LED1,"led1"))
    {
        printk("gpio_request failed\n");
        return -EINVAL;
    }

    gpio_direction_output(LED1,0);
    return 0;
}

static void __exit led_drv_exit(void)
{
    gpio_free(LED1);
    cdev_del(ledCdev);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
